using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Web;
using System.Web.UI;
using System.Windows.Forms;
using Control = System.Web.UI.Control;

namespace WebForms.ControlExtender
{
    // persist properties, not controls
    [ParseChildren(true, "TargetProperties"), PersistChildren(false)]
    public abstract class ControlExtender<T> : Control, IExtenderProvider where T : TargetControlProperties, new()
    {

        private TargetControlPropertiesCollection<T> _targetProperties = new TargetControlPropertiesCollection<T>();

        #region Designer Hack

        protected void NotifyDesignerOfChange()
        {
            // Thanks to Paul Easter for this code on microsoft.public.dotnet.framework.aspnet.buildingcontrols

            // Tell the designer that the component has changed
            if (Site != null && Site.DesignMode)
            {
                try
                {
                    IDesignerHost host = (IDesignerHost)Site.GetService(typeof(IDesignerHost));
                    if (host != null)
                    {
                        IComponentChangeService changer =
                            (IComponentChangeService)host.GetService(typeof(IComponentChangeService));
                        if (changer != null)
                        {
                            //changer.OnComponentChanging(this, null);
                            changer.OnComponentChanged(this, null, null, null);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Problem notifying designer of change to DefaultButtons." + Environment.NewLine +
                                    ex.Message);
                    Debug.WriteLine(ex.ToString());
                }
            }
        }

        #endregion

        #region Control Implementation

        [Browsable(false),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
         NotifyParentProperty(true),
         PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public TargetControlPropertiesCollection<T> TargetProperties
        {
            get
            {
                if (_targetProperties == null)
                {
                    _targetProperties = new TargetControlPropertiesCollection<T>();
                    if (IsTrackingViewState)
                    {
                        ((IStateManager) _targetProperties).TrackViewState();
                    }
                }
                return _targetProperties;
            }
        }

        /// <summary>
        ///   Overrides <see cref = "Control.Visible" /> to hide it.
        /// </summary>
        [Browsable(false)]
        public override Boolean Visible
        {
            get { return base.Visible; }
            set { base.Visible = value; }
        }

        /// <summary>
        ///   Overrides <see cref = "Control.OnPreRender" />.
        /// </summary>
        /// <param name = "e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (_targetProperties == null || _targetProperties.Count == 0)
            {
                return;
            }

            if (!PropertiesValid)
            {
                throw new HttpException("One or more settings are invalid for '" + ID + "'");
            }
        }

        /// <summary>
        ///   Overrides <see cref = "Control.Render" />.
        /// </summary>
        /// <remarks>
        ///   No rendering is done for this control.
        /// </remarks>
        protected override void Render(HtmlTextWriter writer)
        {
        }

        #endregion

        #region Property Validation

        private Boolean _propertiesChecked;
        private Boolean _propertiesValid;

        /// <summary>
        ///   Ensures that the current properties are valid.
        /// </summary>
        protected virtual Boolean PropertiesValid
        {
            get
            {
                if (!(_propertiesChecked))
                {
                    _propertiesValid = ControlPropertiesValid();
                    _propertiesChecked = true;
                }
                return _propertiesValid;
            }
        }

        /// <summary>
        ///   Examines the current properties to ensure their validity.
        /// </summary>
        protected virtual bool ControlPropertiesValid()
        {
            foreach (T item in TargetProperties)
            {
                if (FindControlInContainers(item.ControlId) == null)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        ///   Finds the given control by id.
        /// </summary>
        private Control FindControlInContainers(String controlName)
        {
            return NamingContainer.FindControl(controlName);
        }

        #endregion

        #region Custom State Management Implementation

        /// <summary>
        ///   Overrides <see cref = "Control.LoadViewState" />
        /// </summary>
        protected override void LoadViewState(object savedState)
        {
            object baseState = null;
            object[] myState = null;

            if (savedState != null)
            {
                myState = (object[])savedState;
                if (myState.Length != 2)
                {
                    throw new ArgumentException("Invalid view state");
                }

                baseState = myState[0];
            }

            base.LoadViewState(baseState);

            if ((myState != null) && (myState[1] != null))
            {
                ((IStateManager)TargetProperties).LoadViewState(myState[1]);
            }
        }

        /// <summary>
        ///   Overrides <see cref = "Control.SaveViewState" />
        /// </summary>
        protected override object SaveViewState()
        {
            object baseState = base.SaveViewState();
            object settingsState = null;

            if ((_targetProperties != null) && (_targetProperties.Count > 0))
            {
                settingsState = ((IStateManager)TargetProperties).SaveViewState();
            }

            if ((baseState != null) || (settingsState != null))
            {
                object[] savedState = new object[2];
                savedState[0] = baseState;
                savedState[1] = settingsState;

                return savedState;
            }

            return null;
        }

        /// <summary>
        ///   Overrides <see cref = "Control.TrackViewState" />
        /// </summary>
        protected override void TrackViewState()
        {
            base.TrackViewState();
            if (_targetProperties != null)
            {
                ((IStateManager)TargetProperties).TrackViewState();
            }
        }

        #endregion Custom State Management Implementation

        #region IExtenderProvider Members

        public abstract Boolean CanExtend(Object target);

        #endregion
    }
}